package com.example.demo.utils;


import com.alibaba.fastjson.JSONObject;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.config.SocketConfig;
import org.apache.http.conn.DnsResolver;
import org.apache.http.conn.HttpConnectionFactory;
import org.apache.http.conn.ManagedHttpClientConnection;
import org.apache.http.conn.routing.HttpRoute;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.entity.ContentType;
import org.apache.http.impl.DefaultConnectionReuseStrategy;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultConnectionKeepAliveStrategy;
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.DefaultHttpResponseParserFactory;
import org.apache.http.impl.conn.ManagedHttpClientConnectionFactory;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.impl.conn.SystemDefaultDnsResolver;
import org.apache.http.impl.io.DefaultHttpRequestWriterFactory;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.SSLContext;
import java.io.IOException;

import java.net.URI;

import java.nio.charset.StandardCharsets;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.concurrent.TimeUnit;


/**
 * @author Created by niugang on 2020/3/25/15:13
 * HttpClient实现应该是线程安全的。 建议将此类的相同实例重用于多个请求执行。
 */
public class HttpClientUtils {

    private static final Logger LOGGER = LoggerFactory.getLogger(HttpClientUtils.class);

    public static CloseableHttpClient httpClient = null;


    /**
     * 获取HttpClient工具类
     *
     * @return CloseableHttpClient
     * @throws Exception Exception
     */
    public static synchronized CloseableHttpClient getHttpClient() throws Exception {
        if (httpClient == null) {

            //使用 loadTrustMaterial() 方法实现一个信任策略，信任所有证书
            SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
                // 信任所有证书
                @Override
                public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                    return true;
                }
            }).build();
            // 该主机名验证程序实际上关闭了主机名验证。 它接受任何有效的SSL会话并匹配目标主机
            SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE);

            //为支持的协议方案创建自定义连接套接字工厂的注册表。
            Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
                    .register("http", PlainConnectionSocketFactory.INSTANCE)
                    .register("https", sslsf)
                    .build();

            //HTTPConnection工厂 ：配置请求/解析响应
            HttpConnectionFactory<HttpRoute, ManagedHttpClientConnection> connFactory = new ManagedHttpClientConnectionFactory(
                    DefaultHttpRequestWriterFactory.INSTANCE, DefaultHttpResponseParserFactory.INSTANCE);
            //DNS解析器
            DnsResolver dnsResolver = SystemDefaultDnsResolver.INSTANCE;

            //创建池化管理器
            PoolingHttpClientConnectionManager manager = new PoolingHttpClientConnectionManager(
                    socketFactoryRegistry, connFactory, dnsResolver);
            // 默认为Socket配置
            SocketConfig socketConfig = SocketConfig.custom()
                    .setTcpNoDelay(true)
                    .build();
            manager.setDefaultSocketConfig(socketConfig);
            // 配置永久连接的最大总数或每个路由限制
            // 可以保留在池中或由连接管理器租用。
            //每个路由的默认最大连接，每个路由实际最大连接为默认为DefaultMaxPreRoute控制，而MaxTotal是控制整个池子最大数
            manager.setMaxTotal(100);
            manager.setDefaultMaxPerRoute(10);
            // 在从连接池获取连接时，连接不活跃多长时间后需要进行一次验证，默认为2s
            manager.setValidateAfterInactivity(5 * 1000);

            //默认请求配置
            //设置连接超时时间 4s
            //设置等待数据超时时间，5s
            //设置从连接池获取连接的等待超时时间
            RequestConfig defaultRequestConfig = RequestConfig.custom()
                    .setConnectTimeout(4 * 1000)
                    .setSocketTimeout(5 * 1000)
                    .setConnectionRequestTimeout(2000)
                    .build();

            httpClient = HttpClients.custom()
                    .setConnectionManager(manager)
                    .setDefaultRequestConfig(defaultRequestConfig)
                    //连接池不是共享模式
                    .setConnectionManagerShared(false)
                    .evictIdleConnections(60, TimeUnit.SECONDS)
                    //定期回收空闲连接
                    .evictExpiredConnections()
                    .setConnectionTimeToLive(60, TimeUnit.SECONDS)
                    //连接重用策略，即是否能keeplive
                    .setConnectionReuseStrategy(DefaultConnectionReuseStrategy.INSTANCE)
                    //长连接配置，即获取长连接生产多长时间
                    .setKeepAliveStrategy(DefaultConnectionKeepAliveStrategy.INSTANCE)
                    //设置重试次数，默认为3次;当前禁用掉(根据需要重启)
                    .setRetryHandler(new DefaultHttpRequestRetryHandler(0, false))
                    .build();
            // jvm 停止或重启时，关闭连接池释放连接资源(跟数据库连接池类似)
            Runtime.getRuntime().addShutdownHook(new Thread() {
                @Override
                public void run() {
                    try {
                        httpClient.close();
                    } catch (IOException e) {
                        LOGGER.error("HttpClient close exception", e);
                    }
                }
            });

        }

        return httpClient;
    }


    public static void main(String[] args) throws Exception {
        //####1.HTTPS get请求验证
        URI uri = new URIBuilder()
                .setScheme("https")
                .setHost("11.12.115.104")
                .setPort(8443)
                .setPath("/serverStatus/serverInfo")
                .build();

        HttpGet httpsget = new HttpGet(uri);
        // 自定义响应处理器
        ResponseHandler<String> responseHandler = new ResponseHandler<String>() {
            @Override
            public String handleResponse(
                    final HttpResponse response) throws IOException {
                int status = response.getStatusLine().getStatusCode();
                if (status >= 200 && status < 300) {
                    HttpEntity entity = response.getEntity();
                    return entity != null ? EntityUtils.toString(entity, StandardCharsets.UTF_8) : null;
                } else {
                    throw new ClientProtocolException("Unexpected response status: " + status + "->" + response.getStatusLine().getReasonPhrase());
                }
            }

        };
        String execute = getHttpClient().execute(httpsget, responseHandler);
        LOGGER.info("https get excute result {}", execute);
        //####2.Https Post请求验证  请求体为json数据

        JSONObject jsonObject = new JSONObject();
        jsonObject.put("user", "3333");
        jsonObject.put("ip", "192.168.1.3");
        jsonObject.put("status", "0");
        ByteArrayEntity byteArrayEntity = new ByteArrayEntity(jsonObject.toJSONString().getBytes(), ContentType.create("application/json", "UTF-8"));

        HttpPost httpspost = new HttpPost("https://11.12.115.104/api/accessControl/saveDevice.do");
        httpspost.setHeader("token", "63be0354c964fffd481f5dc5b5ca25e7");
        httpspost.setEntity(byteArrayEntity);

        String httpsGostExecute = getHttpClient().execute(httpspost, responseHandler);

        LOGGER.info("https post excute result {}", httpsGostExecute);
        //####3.http get

        HttpGet httpGet = new HttpGet("http://www.baidu.com");
        String httpGetExecute = getHttpClient().execute(httpGet, responseHandler);

        LOGGER.info("http get excute result {}", httpGetExecute);


    }
}
